package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstant;
import com.zzyl.constant.Constants;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.LoginDto;
import com.zzyl.dto.UserDto;
import com.zzyl.entity.Resource;
import com.zzyl.entity.User;
import com.zzyl.entity.UserRole;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeptMapper;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.mapper.UserMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.properties.JwtTokenManagerProperties;
import com.zzyl.properties.SecurityConfigProperties;
import com.zzyl.service.UserService;
import com.zzyl.utils.JwtUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.UserVo;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;

    private final DeptMapper deptMapper;

    private final UserRoleMapper userRoleMapper;

    private final ResourceMapper resourceMapper;

    private final SecurityConfigProperties securityConfigProperties;

    private final StringRedisTemplate redisTemplate;

    private final JwtTokenManagerProperties jwtTokenManagerProperties;

    /**
     * 查询用户列表
     * @param userDto
     * @return
     */
    @Override
    public List<UserVo> list(UserDto userDto) {
        return userMapper.selectAll(userDto.getRealName());
    }

    /**
     * 新增用户
     * @param userDto
     */
    @Transactional
    @Override
    public Boolean add(UserDto userDto) {
        //判断是否是最底层部门
        if (!isLowestDept(userDto.getDeptNo())){
            throw new BaseException(BasicEnum.USER_LOCATED_BOTTOMED_DEPT);
        }
        //拷贝属性
        User user = BeanUtil.copyProperties(userDto, User.class);
        //密码加密
        String password = BCrypt.hashpw(SuperConstant.DEFAULT_PASSWORD, BCrypt.gensalt());
        user.setPassword(password);
        //附加属性
        user.setUsername(user.getEmail());
        user.setDataState(SuperConstant.DATA_STATE_0);
        user.setNickName(user.getRealName());
        //保存中间表
        List<UserRole> userRoleList = new ArrayList<>();
        user.getRoleVoIds().forEach(roleId -> userRoleList.add(UserRole.builder()
                .userId(user.getId())
                .roleId(Long.valueOf(roleId))
                .dataState(SuperConstant.DATA_STATE_0)
                .build()));
        userRoleMapper.batchInsert(userRoleList);
        //新增
        int result = userMapper.insert(user);
        return result > 0;
    }

    /**
     * 分页查询用户列表
     * @param userDto
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public PageResponse<UserVo> page(UserDto userDto, Integer pageNum, Integer pageSize) {
        //完成分页
        PageHelper.startPage(pageNum, pageSize);
        //查询
        if (userDto.getPostNo() != null) {
            userDto.setPostNo(NoProcessing.processString(userDto.getPostNo()));
        }
        assert userDto.getDeptNo() != null;
        userDto.setDeptNo(NoProcessing.processString(userDto.getDeptNo()));
        Page<UserVo> pageInfo = userMapper.pageInfo(userDto);
        //返回
        return PageResponse.of(pageInfo, UserVo.class);
    }

    /**
     * 修改用户
     * @param userDto
     * @return
     */
    @Override
    public Boolean update(UserDto userDto) {
        //拷贝属性
        User user = BeanUtil.copyProperties(userDto, User.class);
        //修改
        Integer result = userMapper.update(user);
        return result > 0;
    }

    /**
     * 修改用户状态
     * @param id
     * @param status
     */
    @Override
    public void updateStatus(Long id, String status) {
        User user = new User();
        user.setId(id);
        user.setDataState(status);
        userMapper.update(user);
    }

    /**
     * 删除用户
     * @param userIds
     */
    @Override
    public void delete(Long userIds) {
        User user = userMapper.selectByPrimaryKey(userIds);
        if (null != user && SuperConstant.DATA_STATE_0.equals(user.getDataState())){
            throw new BaseException(BasicEnum.ENABLED_CANNOT_DELETED);
        }
        //逻辑删除
        userMapper.setDeleteState(userIds);
    }

    /**
     * 重置密码
     * @param userId
     * @return
     */
    @Override
    public Boolean reset(Long userId) {
        //TODO
        User user = userMapper.selectByPrimaryKey(userId);
        //Integer result = userMapper.reset(userId);
        return userMapper.update(user) > 0;
    }

    /**
     * 管理端用户登录
     * @param loginDto
     * @return
     */
    @Override
    public UserVo login(LoginDto loginDto) {
        //查询数据库,判断是否存在登录信息
        User user = userMapper.selectUserByUsername(loginDto.getUsername());
        //如果不存在
        if (ObjectUtil.isEmpty(user)){
            throw new BaseException(BasicEnum.USERNAME_NOT_EXISTS);
        }
        //校验密码
        if (!BCrypt.checkpw(loginDto.getPassword(), user.getPassword())){
            throw new BaseException(BasicEnum.PASSWORD_ERROR);
        }
        //判断是否被禁用
        if (SuperConstant.DATA_STATE_1.equals(user.getDataState())){
            throw new BaseException(BasicEnum.USER_DISABLED);
        }
        //用户授权与鉴权
        UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
        //获取用户资源路径
        List<String> pathList = resourceMapper.selectResourceListByUserId(userVo.getId());
        //获取配置中的白名单
        List<String> publicAccessUrls = securityConfigProperties.getPublicAccessUrls();
        //合并
        publicAccessUrls.addAll(pathList);
        //存入Redis
        redisTemplate.opsForValue().set(CacheConstant.PUBLIC_ACCESS_URLS + userVo.getId(),
                                        JSONUtil.toJsonStr(publicAccessUrls),
                                        jwtTokenManagerProperties.getTtl(), TimeUnit.SECONDS);

        //登录成功,生成JWT令牌
        Map<String, Object> claims = new HashMap<>();
        //用户信息封装进claims
        claims.put(Constants.JWT_CURRENT_USER, JSONUtil.toJsonStr(userVo));//注意转JSON
        //生成令牌
        String userToken = JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(),
                jwtTokenManagerProperties.getTtl(), claims);
        userVo.setUserToken(userToken);
        //密码脱敏
        userVo.setPassword(null);
        //返回
        return userVo;
    }

    /**
     * 判断是否是最底层部门
     *
     * @return
     */
    private boolean isLowestDept(String deptNo) {
        int count = deptMapper.isLowestDept(deptNo);
        return count > 0;
    }
}
